/**
 * Copyright 2010 The Regents of the University of California
 *
 *  All Rights Reserved
 */
/**
 * File: fault-detect.c
 *
 * This file implements the functionality for liveness detection for the fat tree
 */

// include files
#include <config.h>
#include <inttypes.h>
#include <unistd.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <limits.h>
#include <pthread.h>
#include <sys/time.h>

#include "fault-detect.h"

#include "openflow/openflow.h"
#include "data-structures.h"
#include "ofpbuf.h"
#include "dcswitch.h"
#include "random.h"
#include "vconn.h"
#include "learning-switch.h"
#include "rconn.h"
#include "vlog.h"
#include "timeval.h"
#include "xtoxll.h"


//////////////////////////////////////////////////////////
// public variables
//////////////////////////////////////////////////////////

//Some of these should shift when additional modules are added
uint8_t keepalive_neighbors[MAX_NUMBER_OF_SWITCHES][ETH_ADDR_LEN];
uint8_t eth_hosts[NUM_HOSTS][ETH_ADDR_LEN];
uint32_t nw_hosts[NUM_HOSTS];

int keepalive_matrix[MAX_NUMBER_OF_SWITCHES][MAX_NUMBER_OF_SWITCHES];
static struct timeval keepalive_neighbor_monitor[MAX_NUMBER_OF_SWITCHES];

int dist_list[MAX_NUMBER_OF_SWITCHES];
int prev_vertex_list[MAX_NUMBER_OF_SWITCHES];
int my_next_hop[K];


/**
 * set to true if keepalive_init has been invoked
 */
static bool isInitialized =false;

/**
 * tracks whether a keep-alive from a neighbor was received
 * on a particular port
 */
static uint8_t _localLinksKeepAlive[K];

/**
 * tracks the last update from the fabric manager
 * regarding a graph edge
 */
struct timeval  mod_time[MAX_NUMBER_OF_SWITCHES][MAX_NUMBER_OF_SWITCHES];

static void keepalive_ds_init(void);

static int me;

bool valueIn(int, int);

void keepalive_init()
{
    int i;
    int nextHopSwitch;

    // initialize local Links
    for (i = 0; i< K; i++) {

        nextHopSwitch = find_next_hop_switch_index(me, i);

        if (nextHopSwitch < 0) {

            // downward facing port towards an end-host
            _localLinksKeepAlive[i] = 255;

        } else {
            // lets set the link to be down initially

            _localLinksKeepAlive[i] = 0;
        }
    }

    keepalive_ds_init();

    isInitialized = true;

    //VLOG_DBG("[%s] Ended\n",__PRETTY_FUNCTION__);
}

//Note that there is no error checking!
//partly because we have aimed at hardcoding the pseudomac for
//the first version
void convert_uint64_to_nmacl(uint64_t hmacl, uint8_t *nmacl)
{
        uint64_t temp = ntohll(hmacl) >> 16; //Radhika: Possible bug?
        memcpy(nmacl, &temp, 6);
}

static void keepalive_ds_init(void)
{
        int i, j;
	int core_switch_count;
        struct timezone tz;
    struct timeval result,now, delay;
    int rc;

    for (i = 0; i < MAX_NUMBER_OF_SWITCHES; i++)
    {
        int flag = 0;
        for (j = 0; j < MAX_NUMBER_OF_SWITCHES; j++)
        {
            keepalive_matrix[i][j] = -1;
            gettimeofday(&(mod_time[i][j]), &tz);
            //aggregation to edge
            if ( i < K*K && j < K*K)
            {
                //if ( (i % K == 0 ||  i % K == 1 || i % K == 2 ) && ( (j -3) % K == 0 || (j-3) % K == 1 || (j-3) % K == 2))
                if ( (valueIn((i % K), K/2) ) && (valueIn((j - (K/2)) % K , (K/2))))
                {
                    if ( j >= i + 1 && j <= i+(K-1))
                    {
                        flag = 1;
                    }
                }
                //else if ( (j % K == 0 ||  j % K == 1 || j % K == 2 ) && ( (i -3) % K == 0 || (i-3) % K == 1 || ( i - 3) % K == 2))
                else if ( (valueIn((j % K), K/2) ) && (valueIn((i - (K/2)) % K , (K/2))))
                {
                    if ( i >= j+1 && i <= j+(K-1))
                    {
                        flag = 1;
                    }
                }
                else
                {
                    flag = 0;
                }
                if (flag)
                {
                    // keepalive_matrix[i][j] = 1;
                    keepalive_matrix[i][j] = DOWN;
                }
            }
       }
    }


    for(core_switch_count = 0 ; core_switch_count < K ; core_switch_count++)
    {
        for (i = core_switch_count*K + K/2 ; i < (core_switch_count + 1) * K; i++)
        {
	    for(j=K*K + ((i%(K/2)))*(K/2) ; j < K*K + ((i%(K/2))+1)*(K/2) ; j++)
	    {
	        keepalive_matrix[i][j] = keepalive_matrix[j][i] = DOWN;
	        //printf("keepalive_matrix[%d][%d] = keepalive_matrix[%d][%d] = DOWN\n",i,j,j,i);
	    }
        }
    }

    for (i = 0 ; i < MAX_NUMBER_OF_SWITCHES; i++)
    {
        for (j = 0; j < MAX_NUMBER_OF_SWITCHES; j++)
        {
            printf("%2d ", keepalive_matrix[i][j]);
        }
        printf("\n");
    }
        


    gettimeofday(&now,NULL);


    for (i = 0; i < MAX_NUMBER_OF_SWITCHES; i++) {
        keepalive_neighbor_monitor[i] = result;
    }
}

receive_fault_notification(int node1, int node2, int state)
{
    // printf("\n [%s] : Got MATRIX Update: %d %d %d", __PRETTY_FUNCTION__, node1, node2, state);
        struct timezone tz;
    if (state == DOWN || state== UP)
    {
        if ((keepalive_matrix[node1][node2] != -1) && !((keepalive_matrix[node1][node2] == keepalive_matrix[node2][node1]) &&( keepalive_matrix[node1][node2] == state)))
        {
            // there was a state change
            //doing the flow changes for both positive and negative information
            //we might want to do flow changes only for negative information
            // printf("\n [%s] : Got MATRIX Update: %d %d %d", __PRETTY_FUNCTION__, node1, node2, state);
            keepalive_matrix[node1][node2] = state;
            keepalive_matrix[node2][node1] = state;
            gettimeofday(&(mod_time[node1][node2]), &tz);
            mod_time[node2][node1] = mod_time[node1][node2];
            //change_flow = 1;
            compute_shortest_path(node1);


        }
    }
}

void compute_shortest_path(int mySwitchIndex)
{
    int i;
    int visited_list[MAX_NUMBER_OF_SWITCHES];

    int cur_vertex = mySwitchIndex;
    int visited_cntr = 0;

    int min_not_visited = -1;
    int min_dist=INF;

    //Initialize dist_list
    for(i=0; i<MAX_NUMBER_OF_SWITCHES; i++)
    {
        dist_list[i]= INF;
    }
    dist_list[mySwitchIndex]=0;

    //Initialize prev_vertex_list
    for(i=0; i<MAX_NUMBER_OF_SWITCHES; i++)
    {
        prev_vertex_list[i]= -1;
    }

    //Initialize visited_list
    for(i=0; i<MAX_NUMBER_OF_SWITCHES; i++)
    {
        visited_list[i]= 0;
    }

    do
    {
        min_not_visited = -1;
        min_dist=INF;
        visited_list[cur_vertex]=1;
        visited_cntr++;
        for(i=0; i<MAX_NUMBER_OF_SWITCHES; i++)
        {
            //Need to code up how to access the fault matrix to be accessed
            if(keepalive_matrix[cur_vertex][i] == UP)
            {
                if(dist_list[i]>(dist_list[cur_vertex]+1))
                {
                    dist_list[i]=dist_list[cur_vertex]+1;
                    prev_vertex_list[i] = cur_vertex;
                }
            }
            if(!visited_list[i] && min_dist>= dist_list[i])
            {
                if(min_dist !=INF && min_dist==dist_list[i])
                {
                    min_dist = dist_list[i];
                    min_not_visited = i;
                }
                else
                {
                    min_dist = dist_list[i];
                    min_not_visited = i;
                }
            }
        }
        cur_vertex = min_not_visited;
    }
    while((visited_cntr != MAX_NUMBER_OF_SWITCHES));
        // printf("[%s] - completed\n", __PRETTY_FUNCTION__);
}

void unicast_init(int mySwitchIndex)
{
    int i,j;

    if ((mySwitchIndex % K < K/2) && (mySwitchIndex < CORE_START))
    {
        j=K/2;
        for (i = 0; i < K/2; i++)
        {
            my_next_hop[i] = -1;
        }
    }
    else
    {
        j=0;
    }
    for (i = 0; i < MAX_NUMBER_OF_SWITCHES; i++)
    {
        if (keepalive_matrix[mySwitchIndex][i] != -1)
        {
            my_next_hop[j] = i;
            j++;
        }
    }
        if ( mySwitchIndex % K < K/2)
        {
                //populate_arp_cache_with_my_hosts();         
        }
        compute_shortest_path(mySwitchIndex);
}
                                                           

bool valueIn(int value, int lim)
{
    int i;
    for (i = 0; i < lim; i++)
    {
	if(value == i)
            return true;
    }
    return false;
}
 
/**
 * dumps the fault graph to stdout
 */
void Fault_Detect_dumpFaultGraph(void) {

    int i,j;

    VLOG_DBG("[%s] Started\n",__PRETTY_FUNCTION__);

    for(i=0;i<getMaxSwitches();i++) {

        for(j=0;j<getMaxSwitches();j++) {
            if (keepalive_matrix[i][j] != -1) {
                // printf("\tedge (%d,%d) state = %d\n",i,j, keepalive_matrix[i][j]);
            }
        }
    }

    VLOG_DBG("[%s] Ended\n",__PRETTY_FUNCTION__);
}

